<?php
//
// +------------------------------------------------------------------------+
// | Gecko Framework                                                        |
// +------------------------------------------------------------------------+
//
require_once("Gecko/Form/Exception.php");
require_once("Gecko/Form/Field.php");

/**
 * Hash control for preventing CSRF attacks, based upon
 * Zend_Form_Hash control
 *
 * @package Gecko.Form.Field;
 * @author Christopher Valderrama <valderrama.christopher@gmail.com>
 * @copyright Copyright (c) 2008
 * @version $Id$v1.0$ 2008
 * @access public
 **/
class Gecko_Form_Field_Hash extends Gecko_Form_Field {
	/**
	 * The type of the control
	 *
	 * @var string
	 */
	protected $type = 'hash';

	/**
	 * The salt to encode the hash
	 *
	 * @var string
	 */
	protected $_salt = 'salt';

	/**
	 * The actual hash
	 *
	 * @var string
	 */
	protected $_hash;

	/**
	 * The Zend_Session_Namespace to use
	 *
	 * @var Zend_Session_Namespace
	 */
	protected $_session;

	/**
	 * The timeout for the token
	 *
	 * @var unknown_type
	 */
	protected $_timeout = 300;

	/**
	 * Creates a new instance of the control
	 *
	 * @param string $name The control name
	 * @param mixed $value The value of the control
	 * @param array $settings A optional settings array
	 */
	public function __construct($name, $value = '', $settings = array()) {
		parent::__construct($name, $value, $settings);

		if( isset( $settings['salt'] ) && !empty( $settings['salt'] ) ) {
			$this->setSalt($settings['salt']);
		}

		$this->initCsrfValidator();
	}


    /**
     * Set session object
     *
     * @param  Zend_Session_Namespace $session
     * @return Zend_Form_Element_Hash
     */
    public function setSession($session)
    {
        $this->_session = $session;
        return $this;
    }

    /**
     * Get session object
     *
     * Instantiate session object if none currently exists
     *
     * @return Zend_Session_Namespace
     */
    public function getSession()
    {
        if (null === $this->_session) {
            require_once 'Zend/Session/Namespace.php';
            $this->_session = new Zend_Session_Namespace($this->getSessionName());
        }
        return $this->_session;
    }

    /**
     * Initialize CSRF validator
     *
     * Creates Session namespace, and initializes CSRF token in session.
     * Additionally, adds validator for validating CSRF token.
     *
     * @return Zend_Form_Element_Hash
     */
    public function initCsrfValidator()
    {
        $session = $this->getSession();
        if (isset($session->hash)) {
            $rightHash = $session->hash;
        } else {
            $rightHash = null;
        }

        $validator = new Zend_Validate_Identical($rightHash);

        $this->addValidator($validator, true);
        return $this;
    }

    /**
     * Salt for CSRF token
     *
     * @param  string $salt
     * @return Zend_Form_Element_Hash
     */
    public function setSalt($salt)
    {
        $this->_salt = (string) $salt;
        return $this;
    }

    /**
     * Retrieve salt for CSRF token
     *
     * @return string
     */
    public function getSalt()
    {
        return $this->_salt;
    }

    /**
     * Retrieve CSRF token
     *
     * If no CSRF token currently exists, generates one.
     *
     * @return string
     */
    public function getHash()
    {
        if (null === $this->_hash) {
            $this->_generateHash();
        }
        return $this->_hash;
    }

    /**
     * Get session namespace for CSRF token
     *
     * Generates a session namespace based on salt, element name, and class.
     *
     * @return string
     */
    public function getSessionName()
    {
        return __CLASS__ . '_' . $this->getSalt() . '_' . $this->getName();
    }

    /**
     * Set timeout for CSRF session token
     *
     * @param  int $ttl
     * @return Zend_Form_Element_Hash
     */
    public function setTimeout($ttl)
    {
        $this->_timeout = (int) $ttl;
        return $this;
    }

    /**
     * Get CSRF session token timeout
     *
     * @return int
     */
    public function getTimeout()
    {
        return $this->_timeout;
    }


    /**
     * Initialize CSRF token in session
     *
     * @return void
     */
    public function initCsrfToken()
    {
        $session = $this->getSession();
        $session->setExpirationHops(1);
        $session->setExpirationSeconds($this->getTimeout());
        $session->hash = $this->getHash();
    }

    /**
     * Generate CSRF token
     *
     * Generates CSRF token and stores both in {@link $_hash} and element
     * value.
     *
     * @return void
     */
    protected function _generateHash() {
        $this->_hash = md5(
            mt_rand(1,1000000)
            .  $this->getSalt()
            .  $this->getName()
            .  mt_rand(1,1000000)
        );

        $this->setValue($this->_hash);
    }

    /**
	 * Returns the built control HTML
	 *
	 * @return string
	 */
	public function getControl() {
		$this->initCsrfToken();

		$value = $this->getHash();
		$name = $this->getName();
		$vars = array();
		$template = Gecko_Router::LIBRARY_DIR . "/Assets/Form/Templates/Hidden.php";

		$vars['name'] = $name;
		$vars['value'] = $value;

		$html = Gecko_Template::renderTemplate( $template, $vars, true );
		return $html;
	}
}